﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ContentTypeBase.cs" company="CodePlex">
//   The MIT License
//   Copyright (c) 2013 Julien Blin
//   
//   Permission is hereby granted, free of charge, to any person obtaining a copy
//   of this software and associated documentation files (the "Software"), to deal
//   in the Software without restriction, including without limitation the rights
//   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//   copies of the Software, and to permit persons to whom the Software is
//   furnished to do so, subject to the following conditions:
//   
//   The above copyright notice and this permission notice shall be included in
//   all copies or substantial portions of the Software.
//   
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//   THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace UCodeFirst.ContentTypes
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;

    using Umbraco.Core;
    using Umbraco.Core.Models;
    using Umbraco.Web;

    /// <summary>
    /// Base type to define content types.
    /// You can decorate the type with <see cref="ContentTypeAttribute"/> to enrich the definition.
    /// The Name of the type is used as the Alias of the content type and as the key.
    /// </summary>
    public class ContentTypeBase : MarshalByRefObject
    {
        /// <summary>
        /// Gets the <see cref="IPublishedContent"/> that was used at initialization.
        /// </summary>
        public IPublishedContent PublishedContent { get; internal set; }

        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// Gets or sets the template id.
        /// </summary>
        public int TemplateId { get; set; }

        /// <summary>
        /// Gets or sets the sort order.
        /// </summary>
        public int SortOrder { get; set; }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets the url name.
        /// </summary>
        public string UrlName { get; set; }

        /// <summary>
        /// Gets or sets the document type alias.
        /// </summary>
        public string DocumentTypeAlias { get; set; }

        /// <summary>
        /// Gets or sets the document type id.
        /// </summary>
        public int DocumentTypeId { get; set; }

        /// <summary>
        /// Gets or sets the writer name.
        /// </summary>
        public string WriterName { get; set; }

        /// <summary>
        /// Gets or sets the creator name.
        /// </summary>
        public string CreatorName { get; set; }

        /// <summary>
        /// Gets or sets the writer id.
        /// </summary>
        public int WriterId { get; set; }

        /// <summary>
        /// Gets or sets the creator id.
        /// </summary>
        public int CreatorId { get; set; }

        /// <summary>
        /// Gets or sets the path.
        /// </summary>
        public string Path { get; set; }

        /// <summary>
        /// Gets or sets the create date.
        /// </summary>
        public DateTime CreateDate { get; set; }

        /// <summary>
        /// Gets or sets the update date.
        /// </summary>
        public DateTime UpdateDate { get; set; }

        /// <summary>
        /// Gets or sets the version.
        /// </summary>
        public Guid Version { get; set; }

        /// <summary>
        /// Gets or sets the level.
        /// </summary>
        public int Level { get; set; }

        /// <summary>
        /// Gets or sets the url.
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// Returns the url with domain.
        /// </summary>
        /// <returns>
        /// The Url with domain.
        /// </returns>
        public string UrlWithDomain()
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.UrlWithDomain();
        }

        /// <summary>
        /// Gets the template alias.
        /// </summary>
        /// <returns>
        /// The template alias.
        /// </returns>
        public string GetTemplateAlias()
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.GetTemplateAlias();
        }

        /// <summary>
        /// Gets the strongly-type parent.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to return.
        /// </typeparam>
        /// <returns>
        /// The parent.
        /// </returns>
        public T Parent<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Parent.ToStrongContent<T>();
        }

        /// <summary>
        /// Gets the content ancestor or self of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content node.
        /// </returns>
        public T AncestorOrSelf<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .Select(x => this.PublishedContent.AncestorOrSelf(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>())
                       .FirstOrDefault();
        }

        /// <summary>
        /// Gets the content ancestor or self of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <param name="level">
        /// The level.
        /// </param>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content node.
        /// </returns>
        public T AncestorOrSelf<T>(int level)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.AncestorOrSelf(level).ToStrongContent<T>();
        }

        /// <summary>
        /// Gets the content ancestors of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> Ancestors<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.Ancestors(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content ancestors of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <param name="level">
        /// The level.
        /// </param>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> Ancestors<T>(int level)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.Ancestors(level).Where(y => x.Contains(y.DocumentTypeAlias)))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content ancestors or self of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> AncestorsOrSelf<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.AncestorsOrSelf(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content descendants of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> Descendants<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.Descendants(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content descendants of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <param name="level">
        /// The level.
        /// </param>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> Descendants<T>(int level)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.Descendants(level).Where(y => x.Contains(y.DocumentTypeAlias)))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content descendants or self of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> DescendantsOrSelf<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.DescendantsOrSelf(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content descendants or self of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <param name="level">
        /// The level.
        /// </param>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public IEnumerable<T> DescendantsOrSelf<T>(int level)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .SelectMany(x => this.PublishedContent.DescendantsOrSelf(level).Where(y => x.Contains(y.DocumentTypeAlias)))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>());
        }

        /// <summary>
        /// Gets the content down of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Down<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .Select(x => this.PublishedContent.Down(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>())
                       .FirstOrDefault();
        }

        /// <summary>
        /// Gets the content down of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <param name="number">
        /// The number of Down to perform.
        /// </param>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Down<T>(int number)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Down(number).ToStrongContent<T>();
        }

        /// <summary>
        /// Gets the next content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Next<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .Select(x => this.PublishedContent.Next(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>())
                       .FirstOrDefault();
        }

        /// <summary>
        /// Gets the next content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <param name="number">
        /// The number of Next to perform.
        /// </param>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Next<T>(int number)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Next(number).ToStrongContent<T>();
        }

        /// <summary>
        /// Gets the previous content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Previous<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .Select(x => this.PublishedContent.Previous(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>())
                       .FirstOrDefault();
        }

        /// <summary>
        /// Gets the previous content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <param name="number">
        /// The number of Next to perform.
        /// </param>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Previous<T>(int number)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Previous(number).ToStrongContent<T>();
        }

        /// <summary>
        /// Gets the sibling content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content nodes.
        /// </returns>
        public T Sibling<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .Select(x => this.PublishedContent.Sibling(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>())
                       .FirstOrDefault();
        }

        /// <summary>
        /// Gets the sibling content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <param name="number">
        /// The number of Sibling to perform.
        /// </param>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Sibling<T>(int number)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Sibling(number).ToStrongContent<T>();
        }

        /// <summary>
        /// Gets the up content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Up<T>()
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.GetContentTypeAliases<T>()
                       .Select(x => this.PublishedContent.Up(x))
                       .Distinct()
                       .Select(x => x.ToStrongContent<T>())
                       .FirstOrDefault();
        }

        /// <summary>
        /// Gets the up content of type <typeparamref name="T"/> or one of its child classes.
        /// </summary>
        /// <typeparam name="T">
        /// The type of content type to look for.
        /// </typeparam>
        /// <param name="number">
        /// The number of Up to perform.
        /// </param>
        /// <returns>
        /// The content node.
        /// </returns>
        public T Up<T>(int number)
            where T : ContentTypeBase
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Up(number).ToStrongContent<T>();
        }

        /// <summary>
        /// Gest the index of this content in its parent collection.
        /// </summary>
        /// <returns>
        /// The index.
        /// </returns>
        public int Index()
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.Index();
        }

        /// <summary>
        /// Returns a value indicating whether this content is an ancestor of <paramref name="content"/>.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <returns>
        /// True if this content is an ancestor of <paramref name="content"/>, false otherwise.
        /// </returns>
        public bool IsAncestor(ContentTypeBase content)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsAncestor(content.PublishedContent);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is an ancestor of <paramref name="content"/>.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is an ancestor
        /// of <paramref name="content"/>, <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsAncestor(ContentTypeBase content, string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsAncestor(content.PublishedContent, valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is an ancestor of <paramref name="content"/>,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is an ancestor
        /// of <paramref name="content"/>, <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsAncestor(ContentTypeBase content, string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsAncestor(content.PublishedContent, valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Returns a value indicating whether this content is an ancestor of <paramref name="content"/>, or itself.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <returns>
        /// True if this content is an ancestor of <paramref name="content"/> or itself, false otherwise.
        /// </returns>
        public bool IsAncestorOrSelf(ContentTypeBase content)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsAncestorOrSelf(content.PublishedContent);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is an ancestor of <paramref name="content"/>, or itself.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is an ancestor
        /// of <paramref name="content"/> or itself, <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsAncestorOrSelf(ContentTypeBase content, string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsAncestorOrSelf(content.PublishedContent, valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is an ancestor of <paramref name="content"/>, or itself,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is an ancestor
        /// of <paramref name="content"/> or itself, <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsAncestorOrSelf(ContentTypeBase content, string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsAncestorOrSelf(content.PublishedContent, valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Returns a value indicating whether this content is a descendant of <paramref name="content"/>.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <returns>
        /// True if this content is a descendant of <paramref name="content"/>, false otherwise.
        /// </returns>
        public bool IsDescendant(ContentTypeBase content)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsDescendant(content.PublishedContent);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is a descendant of <paramref name="content"/>.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is a descendant
        /// of <paramref name="content"/>, <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsDescendant(ContentTypeBase content, string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsDescendant(content.PublishedContent, valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is a descendant of <paramref name="content"/>,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is a descendant
        /// of <paramref name="content"/>, <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsDescendant(ContentTypeBase content, string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsDescendant(content.PublishedContent, valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Returns a value indicating whether this content is a descendant of <paramref name="content"/>, or itself.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <returns>
        /// True if this content is a descendant of <paramref name="content"/> or itself, false otherwise.
        /// </returns>
        public bool IsDescendantOrSelf(ContentTypeBase content)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsDescendantOrSelf(content.PublishedContent);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is a descendant of <paramref name="content"/> or itself.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is a descendant
        /// of <paramref name="content"/> or itself, <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsDescendantOrSelf(ContentTypeBase content, string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsDescendantOrSelf(content.PublishedContent, valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is a descendant of <paramref name="content"/> or itself,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="content">
        /// The other content.
        /// </param>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is a descendant
        /// of <paramref name="content"/> or itself, <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsDescendantOrSelf(ContentTypeBase content, string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            content.EnsuredPublishedContent();
            return this.PublishedContent.IsDescendantOrSelf(content.PublishedContent, valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Returns a value indicating whether this content is even considering its position in its parent collection.
        /// </summary>
        /// <returns>
        /// True if this content is even, false otherwise.
        /// </returns>
        public bool IsEven()
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsEven();
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is even considering its position in its parent collection.
        /// </summary>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is even,
        /// <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsEven(string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsEven(valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is even considering its position in its parent collection,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is even,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsEven(string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsEven(valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Returns a value indicating whether this content is first considering its position in its parent collection.
        /// </summary>
        /// <returns>
        /// True if this content is first, false otherwise.
        /// </returns>
        public bool IsFirst()
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsFirst();
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is first considering its position in its parent collection.
        /// </summary>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is first,
        /// <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsFirst(string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsFirst(valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is first considering its position in its parent collection,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is first,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsFirst(string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsFirst(valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Returns a value indicating whether this content is last considering its position in its parent collection.
        /// </summary>
        /// <returns>
        /// True if this content is last, false otherwise.
        /// </returns>
        public bool IsLast()
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsLast();
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is last considering its position in its parent collection.
        /// </summary>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is last,
        /// <see cref="string.Empty"/> otherwise.
        /// </returns>
        public IHtmlString IsLast(string valueIfTrue)
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsLast(valueIfTrue);
        }

        /// <summary>
        /// Returns <paramref name="valueIfTrue"/> if this content is last considering its position in its parent collection,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </summary>
        /// <param name="valueIfTrue">
        /// The value to write if true.
        /// </param>
        /// <param name="valueIfFalse">
        /// The value to write if false.
        /// </param>
        /// <returns>
        /// A <see cref="HtmlString"/> representing <paramref name="valueIfTrue"/> if this content is last,
        /// <paramref name="valueIfFalse"/> otherwise.
        /// </returns>
        public IHtmlString IsLast(string valueIfTrue, string valueIfFalse)
        {
            this.EnsuredPublishedContent();
            return this.PublishedContent.IsLast(valueIfTrue, valueIfFalse);
        }

        /// <summary>
        /// Ensure with an exception that the <see cref="PublishedContent"/> property has been initialized.
        /// </summary>
        protected void EnsuredPublishedContent()
        {
            if (this.PublishedContent == null)
            {
                throw new UCodeFirstException(Resources.PublishedContentNotInitialized);
            }
        }

        /// <summary>
        /// Gets a list of content type aliases using the object hierarchy.
        /// </summary>
        /// <typeparam name="T">
        /// The base type.
        /// </typeparam>
        /// <returns>
        /// The content type aliases.
        /// </returns>
        protected IEnumerable<string> GetContentTypeAliases<T>()
            where T : ContentTypeBase
        {
            return this.GetContentTypeAliases(typeof(T));
        }

        /// <summary>
        /// Gets a list of content type aliases using the object hierarchy.
        /// </summary>
        /// <param name="baseType">
        /// The base type.
        /// </param>
        /// <returns>
        /// The content type aliases.
        /// </returns>
        protected IEnumerable<string> GetContentTypeAliases(Type baseType)
        {
            if (!typeof(ContentTypeBase).IsAssignableFrom(baseType))
            {
                throw new UCodeFirstException(Resources.UnableToFindContentType.InvariantFormat(baseType.Name));
            }

            return TypesScanner.GetClassesInBinThatAreAssignableFrom(baseType, excludebaseType: false).Select(x => x.Name);
        }
    }
}
